package com.stars.easyms.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.stars.easyms.base.constant.CommonConstants;
import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.encrypt.EasyMsEncrypt;
import com.stars.easyms.base.http.EasyMsRequestSynchronizationManager;
import com.stars.easyms.base.util.DateTimeUtil;
import com.stars.easyms.base.util.PatternMatcherUtil;
import com.stars.easyms.gateway.constant.EasyMsGatewayConstants;
import com.stars.easyms.gateway.properties.EasyMsGatewayProperties;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * <p>className: EasyMsResponseTraceFilter</p>
 * <p>description: 记录所有进入gateway的响应日志</p>
 *
 * @author guoguifang
 * @version 1.6.1
 * @date 2020/8/19 14:32
 */
@Slf4j
public class EasyMsResponseFilter implements GlobalFilter, Ordered {

    private EasyMsGatewayProperties gatewayProperties;

    private final Set<String> encryptPermitUrlSet = new HashSet<>();

    private final Set<String> logPermitUrlSet = new HashSet<>();

    public EasyMsResponseFilter(EasyMsGatewayProperties gatewayProperties) {
        this.gatewayProperties = gatewayProperties;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 如果开启了debug模式则把debug的url直接跳过权限校验
        if (exchange.getAttribute(EasyMsGatewayConstants.DEBUG) != null) {
            return chain.filter(exchange);
        }

        //获取response的 返回数据
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();

        // 如果返回的是正常状态的数据则记录日志
        HttpStatus statusCode = originalResponse.getStatusCode();
        if (statusCode == HttpStatus.OK) {
            ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {

                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {

                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {

                        // 将dataBuffer进行合并join后读取到content中
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffers);
                        byte[] responseContent = new byte[join.readableByteCount()];
                        join.read(responseContent);

                        // 释放内存
                        DataBufferUtils.release(join);

                        // 获取到response响应字符串
                        HttpHeaders headers = originalResponse.getHeaders();
                        String responseTime = headers.getFirst(HttpHeaderConstants.HEADER_KEY_RESPONSE_TIME);

                        String traceId = exchange.getAttribute(HttpHeaderConstants.TRACE_KEY);
                        String upstreamRequestId = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_UPSTREAM_REQUEST_ID);
                        EasyMsRequestSynchronizationManager.setTraceInfo(traceId, upstreamRequestId);

                        String currentRequestPath = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_REQUEST_PATH);
                        String downstreamRequestId = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_REQUEST_ID);
                        String requestSys = exchange.getAttribute(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS);

                        String responseContentStr = new String(responseContent, StandardCharsets.UTF_8);


                        // 记录请求日志
                        log.info("[路由-返回响应]-[请求地址:{}]-[请求系统:{}]-[请求ID:{}]-[响应时间:{}]-[接收响应时间:{}]-响应数据:{}",
                                currentRequestPath, requestSys, downstreamRequestId, responseTime, DateTimeUtil.now(),
                                isLogPermitUrl(currentRequestPath) ? "***" : responseContentStr);

                        EasyMsRequestSynchronizationManager.clearTraceInfo();

                        // 如果需要加密则进行加密
                        if (gatewayProperties.getEncrypt().isEnabled()
                                && !isEncryptPermitUrl(currentRequestPath) && exchange.getAttribute(EasyMsGatewayConstants.SWAGGER_DEBUG) == null) {
                            Object encryptedResponseContent = EasyMsEncrypt.encrypt(responseContentStr,
                                    gatewayProperties.getEncrypt().getSecret(), gatewayProperties.getEncrypt().getIv(),
                                    gatewayProperties.getEncrypt().getResponseKey());
                            responseContent = JSON.toJSONString(encryptedResponseContent).getBytes(StandardCharsets.UTF_8);
                        }

                        // 重新返回content
                        originalResponse.getHeaders().setContentLength(responseContent.length);
                        return bufferFactory.wrap(responseContent);
                    }));
                }
            };
            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -10;
    }


    @PostConstruct
    private void init() {
        // 获取不需要做加解密的url的集合
        if (StringUtils.hasText(gatewayProperties.getEncrypt().getRequestPermitUrl())) {
            Collections.addAll(encryptPermitUrlSet,
                    StringUtils.tokenizeToStringArray(gatewayProperties.getEncrypt().getRequestPermitUrl(), CommonConstants.PROPERTIES_SEPARATOR));
        }
        if (StringUtils.hasText(gatewayProperties.getEncrypt().getResponsePermitUrl())) {
            Collections.addAll(encryptPermitUrlSet,
                    StringUtils.tokenizeToStringArray(gatewayProperties.getEncrypt().getResponsePermitUrl(), CommonConstants.PROPERTIES_SEPARATOR));
        }
        if (StringUtils.hasText(gatewayProperties.getLog().getResponsePermitUrl())) {
            Collections.addAll(logPermitUrlSet,
                    StringUtils.tokenizeToStringArray(gatewayProperties.getLog().getResponsePermitUrl(), CommonConstants.PROPERTIES_SEPARATOR));
        }
    }

    private boolean isEncryptPermitUrl(String requestPath) {
        for (String encryptPermitUrl : encryptPermitUrlSet) {
            if (PatternMatcherUtil.doUrlPatternMatch(encryptPermitUrl, requestPath, true)) {
                return true;
            }
        }
        return false;
    }

    private boolean isLogPermitUrl(String requestPath) {
        for (String logPermitUrl : logPermitUrlSet) {
            if (PatternMatcherUtil.doUrlPatternMatch(logPermitUrl, requestPath, true)) {
                return true;
            }
        }
        return false;
    }

}